/*
    This source file is part of GeNa project.
    Copyright © GeNa project 2008
    For the latest info, see http://code.google.com/p/genaproject/ or  http://lumpyproject.forums-rpg.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>. */

#ifndef VECTOR_H_INCLUDED
#define VECTOR_H_INCLUDED

#include <boost/operators.hpp>
#include <boost/serialization/array.hpp>
#ifndef WX_PCH_H_INCLUDED
#include "wx_pch.h"
#endif

#include <cfloat>

using namespace boost;

#include "multiindicearray.h"

namespace math
{

template<class T> class Matrix3;
template<class T> class Matrix4;
template<class T> class Quaternion;
template<class T> class Vector4;

template<class T> class Vector3 :   equality_comparable< Vector3<T>,
                                    addable< Vector3<T>,
                                    subtractable< Vector3<T>,
                                    multipliable< Vector3<T>,
                                    dividable< Vector3<T>,
                                    multipliable2< Vector3<T>, T,
                                    dividable2< Vector3<T>, T
                                    > > > > > > >
{
public:
    T* c;
    struct
    {
        T x, y, z;
    };

    static const Vector3 Null;

    Vector3();

    Vector3(T _x, T _y, T _z);

    template<class U> Vector3( const Vector3<U> &v);

    Vector3 & operator=( const Vector3 &v);

    template<class U> Vector3( const Vector4<U> &v);

    Vector4<T> getPoint() const;

    Vector4<T> getVector() const;


    //Operators

    bool operator==( const Vector3 &v) const; //You should not use this test with floating point component !!!

    Vector3 & operator+=(const Vector3 &v);

    Vector3 operator-();

    Vector3 & operator-=(const Vector3 &v);

    Vector3 & operator*=(T k);

    Vector3 & operator*=(const Vector3 &v);

    Vector3 & operator/=(T k);

    Vector3 & operator/=(const Vector3 &v);

    T operator|( const Vector3 &v) const; // Dot product

    Vector3 operator^( const Vector3 &v) const; // Cross product

    Vector3 & operator^=( const Vector3 &v);

    Vector3 operator*(const Matrix3<T> &m) const;

    T squaredLength() const;

    void normalize();

    T length() const;

    template<class Archive> void serialize(Archive & ar, const unsigned int version);
};

template<class T> class Vector4 :   equality_comparable< Vector4<T>,
                                    addable< Vector4<T>,
                                    subtractable< Vector4<T>,
                                    multipliable< Vector4<T>,
                                    dividable< Vector4<T>,
                                    multipliable< Vector4<T>, T,
                                    dividable< Vector4<T>, T
                                    > > > > > > >
{
public:
    union
    {
        T c[4];
        T x, y, z, w;
    };

    static const Vector4 Null;

    Vector4();

    Vector4(T _x, T _y, T _z, T _w);
    template<class U> Vector4( const Vector4<U> &v);

    Vector4 & operator=( const Vector4 &v);

    //Operators

    bool operator==( const Vector4 &v) const; //You should not use this test with floating point component !!!

    Vector4 & operator+=(const Vector4 &v);

    Vector4 operator-();

    Vector4 & operator-=(const Vector4 &v);

    Vector4 & operator*=(T k);

    Vector4 & operator*=(const Vector4 &v);

    Vector4 & operator/=(T k);

    Vector4 & operator/=(const Vector4 &v);

    T operator|( const Vector4 &v) const; // Dot product

    Vector4 operator^( const Vector4 &v) const; // Cross product

    Vector4 operator*(const Matrix4<T> &m) const;

    Vector4 & operator^=( const Vector4 &v);

    void normalize();

    T squaredLength() const;

    T length() const;

    template<class Archive> void serialize(Archive & ar, const unsigned int version);
};

template<class T> Vector3<T>::Vector3() : c(&x)
{
    x = 0;
    y = 0;
    z = 0;
}

template<class T> Vector3<T>::Vector3(T _x, T _y, T _z) : c(&x)
{
    x = _x;
    y = _y;
    z = _z;
}


template<class T> template<class U> Vector3<T>::Vector3( const Vector3<U> &v) : c(&x)
{
    x = v.x;
    y = v.y;
    z = v.z;
}


template<class T> Vector3<T> & Vector3<T>::operator=( const Vector3 &v)
{
    x = v.x;
    y = v.y;
    z = v.z;
    return (*this);
}

template<class T> template<class U> Vector3<T>::Vector3( const Vector4<U> &v) : c(&x)
{
    x = v.x;
    y = v.y;
    z = v.z;
}

template<class T> Vector4<T> Vector3<T>::getPoint() const
{
    return Vector4<T>(x,y,z, 1);
}

template<class T> Vector4<T> Vector3<T>::getVector() const
{
    return Vector4<T>(x,y,z,0);
}

//Operators

template<class T> bool Vector3<T>::operator==( const Vector3 &v) const //You should not use this test with floating point component !!!
{
    return (x == v.x) && (y == v.y) && (z == v.z);
}

template<class T> Vector3<T> & Vector3<T>::operator+=(const Vector3 &v)
{
    x += v.x;
    y += v.y;
    z += v.z;
    return *this;
}

template<class T> Vector3<T> Vector3<T>::operator-()
{
    return Vector3(-x, -y, -z);
}

template<class T> Vector3<T> & Vector3<T>::operator-=(const Vector3 &v)
{
    x -= v.x;
    y -= v.y;
    z -= v.z;
    return *this;
}

template<class T> Vector3<T> & Vector3<T>::operator*=(T k)
{
    x *= k;
    y *= k;
    z *= k;
    return *this;
}

template<class T> Vector3<T> & Vector3<T>::operator*=(const Vector3 &v)
{
    x *= v.x;
    y *= v.y;
    z *= v.z;
    return *this;
}

template<class T> Vector3<T> & Vector3<T>::operator/=(T k)
{
    x /= k;
    y /= k;
    z /= k;
    return *this;
}

template<class T> Vector3<T> & Vector3<T>::operator/=(const Vector3 &v)
{
    x /= v.x;
    y /= v.y;
    z /= v.z;
    return *this;
}

template<class T> T Vector3<T>::operator|( const Vector3 &v) const // Dot product
{
    return x*v.x + y*v.y + z*v.z;
}

template<class T> Vector3<T> Vector3<T>::operator^( const Vector3 &v) const // Cross product
{
    Vector3 r;
    r.x = y*v.z - z*v.y;
    r.y = z*v.x - x*v.z;
    r.z = x*v.y - y*v.x;
    return r;
}

template<class T> Vector3<T> & Vector3<T>::operator^=( const Vector3 &v)
{
    *this = *this ^ v;
    return *this;
}

template<class T> Vector3<T> Vector3<T>::operator*(const Matrix3<T> &m) const
{
    return Vector3( x*m.m11 + y*m.m21 + z*m.m31,
                    x*m.m12 + y*m.m22 + z*m.m32,
                    x*m.m13 + y*m.m23 + z*m.m33);
}

template<class T> T Vector3<T>::squaredLength() const
{
    return *this | *this;
}

template<class T> void Vector3<T>::normalize()
{
    float l = length();
    x /= l;
    y /= l;
    z /= l;
}

template<class T> T Vector3<T>::length() const
{
    return sqrt( squaredLength());
}

template<class T> template<class Archive> void Vector3<T>::serialize(Archive & ar, const unsigned int version)
{
    ar & make_nvp("l",serialization::make_array(c,3));
}

template< class T> const Vector3<T> Vector3<T>::Null(0,0,0);

template<class T> Vector4<T>::Vector4()
{
    x = 0;
    y = 0;
    z = 0;
    w = 0;
}


template<class T> Vector4<T>::Vector4(T _x, T _y, T _z, T _w)
{
    x = _x;
    y = _y;
    z = _z;
    w = _w;
}


template<class T> template<class U> Vector4<T>::Vector4( const Vector4<U> &v)
{
    x = v.x;
    y = v.y;
    z = v.z;
    w = v.w;
}

template<class T> Vector4<T> & Vector4<T>::operator=( const Vector4 &v)
{
    x = v.x;
    y = v.y;
    z = v.z;
    w = v.w;
    return (*this);
}

//Operators

template<class T> bool Vector4<T>::operator==( const Vector4 &v) const //You should not use this test with floating point component !!!
{
    return (x == v.x) && (y == v.y) && (z == v.z) && (w == v.w);
}

template<class T> Vector4<T> & Vector4<T>::operator+=(const Vector4 &v)
{
    x += v.x;
    y += v.y;
    z += v.z;
    w += v.w;
    return *this;
}

template<class T> Vector4<T> Vector4<T>::operator-()
{
    return Vector4(x,y,z.w);
}

template<class T> Vector4<T> & Vector4<T>::operator-=(const Vector4 &v)
{
    x -= v.x;
    y -= v.y;
    z -= v.z;
    w -= v.w;
    return *this;
}

template<class T> Vector4<T> & Vector4<T>::operator*=(T k)
{
    x *= k;
    y *= k;
    z *= k;
    w *= k;
    return *this;
}

template<class T> Vector4<T> & Vector4<T>::operator*=(const Vector4 &v)
{
    x *= v.x;
    y *= v.y;
    z *= v.z;
    w *= v.w;
    return *this;
}

template<class T> Vector4<T> & Vector4<T>::operator/=(T k)
{
    x /= k;
    y /= k;
    z /= k;
    w /= k;
    return *this;
}

template<class T> Vector4<T> & Vector4<T>::operator/=(const Vector4 &v)
{
    x /= v.x;
    y /= v.y;
    z /= v.z;
    w /= v.w;
    return *this;
}

template<class T> T Vector4<T>::operator|( const Vector4 &v) const // Dot product
{
    return x*v.x + y*v.y + z*v.z + w*v.w;
}

template<class T> Vector4<T> Vector4<T>::operator^( const Vector4 &v) const // Cross product
{
    Vector4 r;
    r.x = y*v.z - z*v.y;
    r.y = z*v.x - x*v.z;
    r.z = x*v.y - y*v.x;
    r.w = 1;
    return r;
}

template<class T> Vector4<T> Vector4<T>::operator*(const Matrix4<T> &m) const
{
    return Vector4( x*m.m11 + y*m.m21 + z*m.m31 + w*m.m41,
                    x*m.m12 + y*m.m22 + z*m.m32 + w*m.m42,
                    x*m.m13 + y*m.m23 + z*m.m33 + w*m.m43,
                    x*m.m14 + y*m.m24 + z*m.m34 + w*m.m44);
}

template<class T> Vector4<T> & Vector4<T>::operator^=( const Vector4 &v)
{
    *this = *this ^ v;
    return *this;
}

template<class T> T Vector4<T>::squaredLength() const
{
    return *this | *this;
}

template<class T> void Vector4<T>::normalize()
{
    float l = length();
    x /= l;
    y /= l;
    z /= l;
    w /= l;
}

template<class T> T Vector4<T>::length() const
{
    return sqrt( squaredLength());
}

template<class T> template<class Archive> void Vector4<T>::serialize(Archive & ar, const unsigned int version)
{
    ar & boost::serialization::make_nvp("l",serialization::make_array(c,4));
}

template< class T> const Vector4<T> Vector4<T>::Null(0,0,0,0);

template<class T> Quaternion<T> getTransformation( Vector3<T> from, Vector3<T> to)
{
    Quaternion<T> q;
    Vector3<T> c = from ^ to;
    q.x = c.x;
    q.y = c.y;
    q.z = c.z;

    q.w = ::sqrt( from.squaredLength() * to.squaredLength()) + (from | to);

    if( q.squaredNorm() <  4*FLT_EPSILON)
    {
        q.x = 0; q.y = 0; q.z = 1; q.w = 0;
    }
    else
        q = q.unitQuaternion();
    return q;
}

} //Namespace math

#include "math/fixed.h"

namespace math
{
typedef Vector3<fixed> vector3q;
typedef Vector4<fixed> vector4q;
typedef Vector3<float> vector3f;
typedef Vector4<float> vector4f;
typedef Vector3<angle> vector3a;
typedef Vector4<angle> vector4a;
} //Namespace math



#endif // VECTOR_H_INCLUDED
